# algorithms/pattern_memory.py
import numpy as np
import pandas as pd
from collections import defaultdict, deque
from .base_algorithm import BaseAlgorithm

class PatternMemoryNetwork(BaseAlgorithm):
    """模式记忆网络 - 基于历史模式匹配的预测算法"""
    
    def __init__(self, lottery_type='ssq', memory_size=1000):
        super().__init__("Pattern-Memory", lottery_type)
        self.memory_size = memory_size
        
        # 不同类型的模式记忆库
        self.consecutive_memory = deque(maxlen=memory_size)
        self.frequency_memory = defaultdict(int)
        self.pattern_similarity_threshold = 0.7
    
    def train(self, historical_data, **kwargs):
        """构建模式记忆库"""
        print(f"[INFO] 构建 {self.algorithm_name} 记忆库...")
        
        try:
            # 构建连续模式记忆
            self._build_consecutive_memory(historical_data)
            
            # 构建频率记忆
            self._build_frequency_memory(historical_data)
            
            self.is_trained = True
            print(f"[INFO] 记忆库构建完成：{len(self.consecutive_memory)} 个连续模式")
            
            return {"status": "success", "message": "记忆库构建完成"}
            
        except Exception as e:
            print(f"[ERROR] 记忆库构建失败: {e}")
            self.is_trained = False
            return {"status": "error", "message": str(e)}
    
    def predict(self, recent_data, num_predictions=1):
        """生成预测结果"""
        predictions = []
        
        try:
            for _ in range(num_predictions):
                # 提取当前模式特征
                current_pattern = self._extract_current_pattern(recent_data)
                
                # 查询相似的历史模式
                similar_patterns = self._query_similar_patterns(current_pattern)
                
                # 基于相似模式生成预测
                if similar_patterns:
                    predicted_numbers = self._generate_from_patterns(similar_patterns)
                else:
                    # 如果没有找到相似模式，使用频率预测
                    predicted_numbers = self._generate_from_frequency()
                
                # 后处理确保格式正确
                red_numbers, blue_numbers = self._post_process_prediction(predicted_numbers)
                
                predictions.append({
                    'red_balls': red_numbers,
                    'blue_balls': blue_numbers
                })
            
            return predictions
            
        except Exception as e:
            print(f"[ERROR] {self.algorithm_name} 预测失败: {e}")
            # 返回默认预测
            default_pred = self._generate_random_prediction()
            return [{
                'red_balls': default_pred['red'],
                'blue_balls': default_pred['blue']
            }]
    
    def calculate_pattern_weights(self, historical_data):
        """计算模式权重"""
        try:
            # 简单的模式权重计算 - 基于频率
            weights = {}
            
            # 计算每个数字的频率权重
            total_frequency = sum(self.frequency_memory.values())
            if total_frequency > 0:
                for num, freq in self.frequency_memory.items():
                    weights[f"number_{num}"] = freq / total_frequency
            
            # 添加其他模式权重
            weights["similarity_threshold"] = self.pattern_similarity_threshold
            weights["memory_size"] = len(self.consecutive_memory)
            
            return weights
            
        except Exception as e:
            print(f"[ERROR] 计算模式权重失败: {e}")
            return {"error": str(e)}
    
    def _build_consecutive_memory(self, historical_data):
        """构建连续模式记忆"""
        pattern_window = 3
        
        for i in range(len(historical_data) - pattern_window):
            try:
                # 提取模式窗口
                window = historical_data[i:i+pattern_window]
                next_result = historical_data[i+pattern_window] if i+pattern_window < len(historical_data) else None
                
                if next_result:
                    # 提取模式特征
                    pattern_signature = self._extract_pattern_signature(window)
                    
                    # 存储到记忆库
                    memory_item = {
                        'pattern': pattern_signature,
                        'next_result': next_result,
                        'period': i,
                        'confidence': self._calculate_pattern_confidence(window, next_result)
                    }
                    
                    self.consecutive_memory.append(memory_item)
                    
            except Exception as e:
                continue  # 跳过有问题的数据
    
    def _build_frequency_memory(self, historical_data):
        """构建频率记忆"""
        for period in historical_data:
            try:
                if isinstance(period, (list, tuple)):
                    for num in period:
                        if isinstance(num, (int, float)):
                            # 根据彩票类型调整数字范围检查
                            if self.lottery_type == 'plw':
                                if 0 <= num <= 9:
                                    self.frequency_memory[int(num)] += 1
                            elif self.lottery_type == 'kl8':
                                if 1 <= num <= 80:
                                    self.frequency_memory[int(num)] += 1
                            else:
                                if 1 <= num <= 33:
                                    self.frequency_memory[int(num)] += 1
            except:
                continue
    
    def predict_single_set(self, recent_data, pattern_analysis=None):
        """基于记忆模式生成预测"""
        try:
            # 提取当前模式特征
            current_pattern = self._extract_current_pattern(recent_data)
            
            # 查询相似的历史模式
            similar_patterns = self._query_similar_patterns(current_pattern)
            
            # 基于相似模式生成预测
            if similar_patterns:
                predicted_numbers = self._generate_from_patterns(similar_patterns)
            else:
                # 如果没有找到相似模式，使用频率预测
                predicted_numbers = self._generate_from_frequency()
            
            # 后处理确保格式正确
            red_numbers, blue_numbers = self._post_process_prediction(predicted_numbers)
            
            return {
                'red': red_numbers,
                'blue': blue_numbers,
                'algorithm': self.algorithm_name,
                'confidence': 0.8 if similar_patterns else 0.4
            }
            
        except Exception as e:
            print(f"[ERROR] {self.algorithm_name} 预测失败: {e}")
            return self._generate_random_prediction()
    
    def _extract_pattern_signature(self, window):
        """从窗口数据提取模式特征签名"""
        signature = []
        
        try:
            for period in window:
                if isinstance(period, (list, tuple)):
                    # 基本统计特征
                    numbers = [n for n in period if isinstance(n, (int, float))]
                    if numbers:
                        avg_num = sum(numbers) / len(numbers)
                        num_range = max(numbers) - min(numbers) if len(numbers) > 1 else 0
                        odd_count = sum(1 for x in numbers if x % 2 == 1)
                        
                        signature.extend([avg_num, num_range, odd_count])
                    else:
                        signature.extend([16.5, 0, 3])  # 默认值
                else:
                    # 单个数字
                    num = float(period) if isinstance(period, (int, float)) else 16.5
                    signature.extend([num, 0, num % 2])
            
            return signature
            
        except Exception:
            return [16.5, 10, 3] * 3  # 默认模式签名
    
    def _extract_current_pattern(self, recent_data):
        """从最近数据提取当前模式"""
        try:
            if isinstance(recent_data, np.ndarray):
                if len(recent_data.shape) > 1:
                    # 取最后几行数据
                    pattern_data = recent_data[-3:]
                    pattern = pattern_data.flatten()[:9]
                else:
                    pattern = recent_data[-9:]
            else:
                pattern = np.array([16.5, 10, 3] * 3)  # 默认模式
            
            # 确保长度为9
            if len(pattern) < 9:
                pattern = np.pad(pattern, (0, 9 - len(pattern)), 'constant', constant_values=0)
            
            return pattern[:9].tolist()
            
        except Exception:
            return [16.5, 10, 3] * 3
    
    def _calculate_pattern_confidence(self, window, next_result):
        """计算模式置信度"""
        try:
            # 简化的置信度计算
            if isinstance(next_result, (list, tuple)) and len(next_result) > 0:
                return 0.8
            else:
                return 0.5
        except:
            return 0.3
    
    def _query_similar_patterns(self, current_pattern):
        """查询相似的历史模式"""
        similar_patterns = []
        
        try:
            for memory_item in self.consecutive_memory:
                stored_pattern = memory_item['pattern']
                
                # 计算相似度
                similarity = self._calculate_similarity(current_pattern, stored_pattern)
                
                if similarity >= self.pattern_similarity_threshold:
                    memory_item['similarity'] = similarity
                    similar_patterns.append(memory_item)
            
            # 如果相似模式太少，降低阈值重新查询
            if len(similar_patterns) < 3:
                for memory_item in self.consecutive_memory:
                    stored_pattern = memory_item['pattern']
                    similarity = self._calculate_similarity(current_pattern, stored_pattern)
                    
                    if similarity >= 0.5:  # 降低阈值
                        memory_item['similarity'] = similarity
                        similar_patterns.append(memory_item)
            
            # 按相似度排序，返回最相似的前10个
            similar_patterns.sort(key=lambda x: x.get('similarity', 0), reverse=True)
            return similar_patterns[:10]
            
        except Exception as e:
            print(f"查询相似模式失败: {e}")
            return []
    
    def _calculate_similarity(self, pattern1, pattern2):
        """计算两个模式的相似度"""
        try:
            # 确保两个模式长度相同
            min_len = min(len(pattern1), len(pattern2))
            if min_len == 0:
                return 0.0
            
            p1 = np.array(pattern1[:min_len])
            p2 = np.array(pattern2[:min_len])
            
            # 计算余弦相似度
            dot_product = np.dot(p1, p2)
            norm_p1 = np.linalg.norm(p1)
            norm_p2 = np.linalg.norm(p2)
            
            if norm_p1 == 0 or norm_p2 == 0:
                return 0.0
            
            similarity = dot_product / (norm_p1 * norm_p2)
            return max(0, similarity)  # 确保非负
            
        except Exception:
            return 0.0
    
    def _generate_from_patterns(self, similar_patterns):
        """基于相似模式生成预测"""
        predicted_numbers = []
        
        try:
            # 收集所有相似模式的下一期结果
            next_results = []
            for pattern_item in similar_patterns:
                next_result = pattern_item['next_result']
                confidence = pattern_item.get('confidence', 0.5)
                
                if isinstance(next_result, (list, tuple)):
                    # 根据置信度权重添加结果
                    weight = max(1, int(confidence * 3))
                    for _ in range(weight):
                        next_results.extend([n for n in next_result if isinstance(n, (int, float)) and 1 <= n <= 33])
                elif isinstance(next_result, (int, float)) and 1 <= next_result <= 33:
                    weight = max(1, int(confidence * 3))
                    for _ in range(weight):
                        next_results.append(int(next_result))
            
            # 统计频率并选择最频繁的数字
            if next_results:
                from collections import Counter
                number_freq = Counter(next_results)
                most_common = number_freq.most_common(10)  # 取前10个最常见的
                
                predicted_numbers = [num for num, freq in most_common]
            
        except Exception as e:
            print(f"从模式生成预测失败: {e}")
        
        # 如果预测数字不够，用频率记忆补充
        return self._supplement_with_frequency(predicted_numbers)
    
    def _generate_from_frequency(self):
        """基于历史频率生成预测"""
        try:
            # 根据彩票类型调整数字范围
            if self.lottery_type == 'plw':
                number_range = (0, 9)
                count = 5
            else:
                number_range = (1, 33)
                count = 7
            
            # 按频率排序选择号码
            if self.frequency_memory:
                sorted_by_freq = sorted(self.frequency_memory.items(), key=lambda x: x[1], reverse=True)
                top_numbers = [num for num, freq in sorted_by_freq[:15]]
                
                if len(top_numbers) >= count:
                    selected = np.random.choice(top_numbers, count, replace=False)
                    return selected.tolist()
            
            # 如果频率记忆不足，使用随机选择
            return np.random.choice(range(number_range[0], number_range[1] + 1), count, replace=False).tolist()
            
        except Exception:
            if self.lottery_type == 'plw':
                return list(range(0, 5))  # 排列5备用预测
            else:
                return list(range(1, 8))  # 备用预测
    
    def _supplement_with_frequency(self, predicted_numbers):
        """用频率数据补充预测数字"""
        try:
            # 根据彩票类型调整数量
            if self.lottery_type == 'plw':
                target_count = 5
                number_range = (0, 9)
            else:
                target_count = 7
                number_range = (1, 33)
            
            # 确保有足够数量的预测数字
            while len(predicted_numbers) < target_count:
                freq_predictions = self._generate_from_frequency()
                for num in freq_predictions:
                    if num not in predicted_numbers:
                        predicted_numbers.append(num)
                        if len(predicted_numbers) >= target_count:
                            break
                            
                # 防止无限循环
                if len(predicted_numbers) < target_count and len(freq_predictions) == 0:
                    remaining = [i for i in range(number_range[0], number_range[1] + 1) if i not in predicted_numbers]
                    if remaining:
                        predicted_numbers.append(np.random.choice(remaining))
                    else:
                        break
            
            return predicted_numbers[:target_count]
            
        except Exception:
            if self.lottery_type == 'plw':
                return list(range(0, 5))
            else:
                return list(range(1, 8))
    
    def _post_process_prediction(self, predicted_numbers):
        """后处理预测结果"""
        try:
            # 根据彩票类型调整处理逻辑
            if self.lottery_type == 'plw':
                # 排列5：5个位置，每个位置0-9的数字
                valid_numbers = [n for n in predicted_numbers if isinstance(n, (int, float)) and 0 <= n <= 9]
                
                if len(valid_numbers) >= 5:
                    # 排列5允许重复数字，不需要去重
                    red_numbers = valid_numbers[:5]
                else:
                    # 补充随机数字
                    red_numbers = valid_numbers.copy()
                    while len(red_numbers) < 5:
                        red_numbers.append(np.random.randint(0, 10))
                
                # 排列5没有蓝球
                blue_numbers = []
            else:
                # 双色球/大乐透：需要去重并补足数量
                valid_numbers = [n for n in predicted_numbers if isinstance(n, (int, float)) and 1 <= n <= 33]
                
                if len(valid_numbers) >= 6:
                    red_numbers = sorted(list(set(valid_numbers))[:6])
                else:
                    # 补充随机数字
                    red_numbers = valid_numbers.copy()
                    remaining = [i for i in range(1, 34) if i not in red_numbers]
                    while len(red_numbers) < 6 and remaining:
                        red_numbers.append(remaining.pop(np.random.randint(len(remaining))))
                    red_numbers = sorted(red_numbers[:6])
                
                # 蓝球预测
                if len(predicted_numbers) > 6:
                    blue_candidate = predicted_numbers[6]
                    if isinstance(blue_candidate, (int, float)) and 1 <= blue_candidate <= 16:
                        blue_numbers = [int(blue_candidate)]
                    else:
                        blue_numbers = [np.random.randint(1, 17)]
                else:
                    blue_numbers = [np.random.randint(1, 17)]
            
            return red_numbers, blue_numbers
            
        except Exception:
            if self.lottery_type == 'plw':
                return list(range(0, 5)), []
            else:
                return sorted(list(range(1, 7))), [1]
    
    def _generate_random_prediction(self):
        """生成随机预测作为备用"""
        if self.lottery_type == 'plw':
            # 排列5：5个位置，每个位置0-9的数字
            red = [np.random.randint(0, 10) for _ in range(5)]
            blue = []
        else:
            # 双色球/大乐透
            red = sorted(np.random.choice(range(1, 34), 6, replace=False))
            blue = [np.random.randint(1, 17)]
        
        return {
            'red': red, 
            'blue': blue,
            'algorithm': self.algorithm_name,
            'confidence': 0.1
        }